fix(channels): restrict private-channel invitations - #4612
Conversation
Require an active owner or admin for third-party private-channel additions in both relay validation and the transactional database authority. Preserve idempotent self-targeting. Co-authored-by: Jordan Mecom <jm@squareup.com> Signed-off-by: Jordan Mecom <jm@squareup.com> Signed-off-by: Eli Foster <efoster@squareup.com>
378c643 to
f428577
Compare
elifoster-block
left a comment
There was a problem hiding this comment.
Client-side follow-up to Jordan's commits: hide the "add people" and invite buttons when you're not an owner/admin of a private channel, and stop mobile from silently dropping your message when the relay refuses the add.
f428577 to
e938a8f
Compare
The relay now rejects a plain member adding another identity to a private channel, but both clients still offered the affordance: - Desktop's members sidebar showed "Add people" to any member, and the non-member mention dialog always offered Invite. Both now consult a shared `canAddChannelMembers` mirror of the relay's kind:9000 authority, and the sidebar explains why the add is unavailable instead of hiding it silently. - Mobile's mention prompt was the only invite path and had no gate at all. `addMembers` also threw out of an `unawaited(send())`, so a relay rejection vanished: the message was never sent and nothing was shown. It now collects per-pubkey failures, always invalidates the members cache, and reports them via `AddMembersException`; the composer surfaces add and send failures in a SnackBar, keeps the draft, and demotes un-added mentions to reference tags so the message still goes out. Also updates VISION.md, which still documented private channels as "invited by member", and records why the self-target escape hatch in `add_member` / `validate_admin_event` is not a role-escalation hole. Tests: unit coverage for both capability mirrors, a mobile widget test for the private-channel agent-add skip, plus e2e coverage that an admin can still invite and that a self-targeted re-add stays idempotent. Signed-off-by: Eli Foster <efoster@squareup.com>
e938a8f to
7364c7d
Compare
wesbillman
left a comment
There was a problem hiding this comment.
Security review by Carl, acting on Wes’s behalf.
I found no blocking issue at head 7364c7d77353234aa468535054876d4471929836.
The authorization boundary is enforced twice and the database remains authoritative. The relay validator rejects a private-channel third-party add unless the signed actor is an active owner/admin, giving clients an immediate rejection. buzz_db::channel::add_member repeats the check inside the existing per-channel advisory-locked transaction, so alternate callers and role/removal races cannot bypass it.
I traced the self-target exception. It only preserves an active member’s idempotent re-add: a non-member/soft-removed identity fails the active-role lookup, and an active member cannot use it to change role because the separate role-change guard requires owner/admin. Private kind:9021 self-join remains rejected before this path.
Owner/admin additions, creator bootstrap, open-channel behavior, and existing huddle/ephemeral flows remain intact. Desktop and mobile mirror the authority only for UX; the relay/DB checks do not trust client state. Mobile now reports partial add failures and demotes identities that were not added to non-notifying reference mentions rather than silently losing the message.
Tests cover regular-member denial, admin invitation, idempotent self-targeting, client capability mirrors, and the mobile rejected-add flow. Exact-head CI is green, including Rust/unit, mobile, security, desktop and relay/backend E2E, cross-compiles, DCO, Semgrep, and zizmor.
Expected behavior change: ordinary members can no longer invite others into private channels. That is the intended security fix, not a regression.
Pull main in before remediation, per Tyler's instruction: no rebase, no force, new commits on top. Co-authored-by: Sami <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz> Signed-off-by: Sami <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz> * origin/main: fix(desktop): remove join API token control (#4897) fix(desktop): allow shared agent mentions (#4913) Polish mobile top navigation (#4778) fix(release): tag immutable desktop candidates (#4811) fix(channels): restrict private-channel invitations (#4612) fix(acp): reject unattended permission requests (#4609) fix(workflow): bind trigger author to the signed event (#4607) fix(git): revoke access for banned relay members (#4608) fix(agent): recover from unsupported image input instead of poisoning the turn (#4896) Define private managed agent wire protocol (#4593) fix(mobile): serialize channel sections sync (#3165) fix(desktop): make missing-command error actionable for released builds (#4802) Signed-off-by: Sami <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
…-overflow-recovery * origin/main: fix(buzz-agent): scope handoff cap per turn, not per session lifetime (#4805) Fix mobile message timeline bounce (#4862) Polish mobile bottom sheets and profile cards (#4911) Fix media attachment actions (#4849) fix(desktop): remove join API token control (#4897) fix(desktop): allow shared agent mentions (#4913) Polish mobile top navigation (#4778) fix(release): tag immutable desktop candidates (#4811) fix(channels): restrict private-channel invitations (#4612) fix(acp): reject unattended permission requests (#4609) fix(workflow): bind trigger author to the signed event (#4607) fix(git): revoke access for banned relay members (#4608) fix(agent): recover from unsupported image input instead of poisoning the turn (#4896) Define private managed agent wire protocol (#4593) fix(mobile): serialize channel sections sync (#3165) fix(desktop): make missing-command error actionable for released builds (#4802) chore(release): release Buzz Desktop version 0.5.5 (#4809) feat: paste composer text without formatting (#4801) Revert "chore(release): release Buzz Desktop version 0.5.5" (#4808) chore(release): release Buzz Desktop version 0.5.5 (#4800) Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> # Conflicts: # crates/buzz-agent/src/agent.rs # crates/buzz-agent/src/handoff.rs # crates/buzz-agent/src/types.rs # crates/buzz-agent/tests/regressions.rs
…usage * origin/main: fix(buzz-agent): scope handoff cap per turn, not per session lifetime (#4805) Fix mobile message timeline bounce (#4862) Polish mobile bottom sheets and profile cards (#4911) Fix media attachment actions (#4849) fix(desktop): remove join API token control (#4897) fix(desktop): allow shared agent mentions (#4913) Polish mobile top navigation (#4778) fix(release): tag immutable desktop candidates (#4811) fix(channels): restrict private-channel invitations (#4612) fix(acp): reject unattended permission requests (#4609) fix(workflow): bind trigger author to the signed event (#4607) fix(git): revoke access for banned relay members (#4608) Co-authored-by: Atish Patel <atish@squareup.com> Signed-off-by: Atish Patel <atish@squareup.com>
…arer-auth * origin/main: (65 commits) fix(desktop): route macos notification clicks (#4799) feat(mobile): sync themes per community (#3767) feat(desktop): sync themes per community (#3653) feat(desktop): cap OpenClaw agent parallelism at 5 (#4019) fix(buzz-agent): scope handoff cap per turn, not per session lifetime (#4805) Fix mobile message timeline bounce (#4862) Polish mobile bottom sheets and profile cards (#4911) Fix media attachment actions (#4849) fix(desktop): remove join API token control (#4897) fix(desktop): allow shared agent mentions (#4913) Polish mobile top navigation (#4778) fix(release): tag immutable desktop candidates (#4811) fix(channels): restrict private-channel invitations (#4612) fix(acp): reject unattended permission requests (#4609) fix(workflow): bind trigger author to the signed event (#4607) fix(git): revoke access for banned relay members (#4608) fix(agent): recover from unsupported image input instead of poisoning the turn (#4896) Define private managed agent wire protocol (#4593) fix(mobile): serialize channel sections sync (#3165) fix(desktop): make missing-command error actionable for released builds (#4802) ... Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> # Conflicts: # CHANGELOG.md
…usage * origin/main: fix(buzz-agent): scope handoff cap per turn, not per session lifetime (#4805) Fix mobile message timeline bounce (#4862) Polish mobile bottom sheets and profile cards (#4911) Fix media attachment actions (#4849) fix(desktop): remove join API token control (#4897) fix(desktop): allow shared agent mentions (#4913) Polish mobile top navigation (#4778) fix(release): tag immutable desktop candidates (#4811) fix(channels): restrict private-channel invitations (#4612) fix(acp): reject unattended permission requests (#4609) fix(workflow): bind trigger author to the signed event (#4607) fix(git): revoke access for banned relay members (#4608) Co-authored-by: Atish Patel <atish@squareup.com> Signed-off-by: Atish Patel <atish@squareup.com>
…ed-agent-store-merge * origin/main: (24 commits) fix(reactions): support max-length custom emoji (#3833) feat(desktop): allow leaving your final community (#3621) fix(buzz-agent): recover from context-window 400s instead of sticking (#4946) docs(persona-pack): fix stale desktop import instructions (#4500) fix(desktop): route macos notification clicks (#4799) feat(mobile): sync themes per community (#3767) feat(desktop): sync themes per community (#3653) feat(desktop): cap OpenClaw agent parallelism at 5 (#4019) fix(buzz-agent): scope handoff cap per turn, not per session lifetime (#4805) Fix mobile message timeline bounce (#4862) Polish mobile bottom sheets and profile cards (#4911) Fix media attachment actions (#4849) fix(desktop): remove join API token control (#4897) fix(desktop): allow shared agent mentions (#4913) Polish mobile top navigation (#4778) fix(release): tag immutable desktop candidates (#4811) fix(channels): restrict private-channel invitations (#4612) fix(acp): reject unattended permission requests (#4609) fix(workflow): bind trigger author to the signed event (#4607) fix(git): revoke access for banned relay members (#4608) ... Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> # Conflicts: # desktop/src-tauri/src/managed_agents/runtime.rs
This change requires an active owner or administrator for third-party additions to private channels. The relay validator and transactional database authority enforce the same rule, including removed-member reactivation and role-change paths.
Idempotent self-target behavior remains available, while ordinary members can no longer extend private-channel access to another identity.
Testing
git diff --check origin/main...codex/security-private-channel-invite-authorityorigin/mainat5c98932Originating Buzz thread:
buzz://message?channel=3928fe05-df61-4b5d-b9c7-d623b9b10ea1&id=3c6c02312f763fbe0d2bfc33a6c1a362f91d0354f3d18b039cf7a0558c1439d1